|
Date : 24 septembre 1992 Logiciel : Data Recovery Software V1.22 Protection : Outils : SOFT-ICE V2.50 Temps pass� : 3 Heures Programme : DRS.EXE Soci�t� : SHAREWARE Divers : Programme �crit en BASIC Microsoft Compil�. Origine : Dr C. JP Num�ro : 185 Le probl�me soumit par Mr X �tait de savoir pourquoi ce logiciel de r�cup�ration de secteurs perdus ou endommag�s insistait lourdement lors de l'affichage pour ne pas afficher compl�tement les octets ( ou plus exactement les mots ) qui comportaient plus d'un 0 hexad�cimal. Exemple, si le mot � afficher �tait 3BC1 ce mot �tait bien affich�. Par contre si le mot �tait nul comme 0000, on affichait que 0. Et dans le cas de 00AD on affichait AD. Pour le mode TEXTE cel� n'avait pas d'importance puisqu'aucun caract�re ASCII n'est repr�sent� par 0. Par contre en notation HEXA c'est beaucoup plus g�nant. N'est-ce pas ? Bien que la notation pr�sent�e dans le programme ne favorise en rien le rep�rage du num�ro d'octet, du d�but ou de la fin de la FAT et/ou des r�pertoires. Le plus dur c'est de comprendre ce que le programmeur a voulu faire. Mais dans le cas pr�sent il s'agit de savoir ce qu'� compil� le compilateur lors de la transcription du BASIC en langage machine. Pourquoi du BASIC ? Parceque les chaines ASCII ne se terminent pas par le signe $. Ce qui �limine l'ASM, il reste donc du PASCAL, BASIC ou du C. En examinant la fin du fichier ( la ou le LINKER laisse des traces ) on trouve les in�vitables instructions BASIC. En d�roulant le programme au pas � pas on s'aper�oit que l'affichage aussi bien en HEXA qu'en ASCII se fait par deux CALL appel�s tour � tour situ�s en CS:83D8 et CS:8406. CS=0DBE CS:83D8 CALL 19CA:0002 CS:8406 CALL 19CA:0002 En fait chaque CALL affiche un octet ( dans le cas o� l'affichage HEXA est s�lectionn�e ). Dans ces CALL l'instruction situ�e en 19CA:0087 charge les caract�res ainsi que leur atribut en B800:offset qui repr�sente comme chacun le sait l'adresse de la m�moire �cran en mode texte. Ces caract�res "arrivent et partent" par des LDSB et MVSB. Il suffit de se positionner sur DS:SI pour savoir d'o� ils viennent. En surveillant la zone d'arriv�e tout en faisant d�filer le programme on d�couvre le "transporteur": il s'agit du CALL 1AD7:5901 appel� en CS:837B. A l'int�rieur de ce CALL on trouve la routine qui transforme les caract�res HEXA en ASCII pour l'affichage puis les places en DS:SI pour �tre exploit�s par les deux CALL 19CA:0002. Dans le CALL 1AD7:5901 les octets arrivent par deux dans le registre BX avec l'instruction MOV BX,[BP+06] situ� en 1AD7:5913. Et en CS:591E se trouve la routine de transcription HEXA --> ASCII pour l'affichage �cran: CS=1AD7 CS:591E FD STD ��> CS:591F 8AC3 MOV AL,BL � CS:5921 22C5 AND AL,CH � CS:5923 0490 ADD AL,90 � CS:5925 27 DAA � CS:5926 1440 ADC AL,40 � CS:5928 27 DAA � CS:5929 AA STOSB � CS:592A FEC4 INC AH � CS:592C 51 PUSH CX � CS:592D 32ED XOR CH,CH � CS:592F D1EA SHR DX,1 <ͻ ; On SHIFT et ROTATE 4 fois � CS:5931 D1DB RCR BX,1 � ; BX et DX. � CS:5933 E2FA LOOP 592F >ͼ � CS:5935 59 POP CX � CS:5936 53 PUSH BX � CS:5937 0BDA OR BX,DX � CS:5939 5B POP BX ��< CS:593A 75E3 JNZ 591F : . : . CS:.... Suite En analysant la fa�on dont on sort de la boucle lorsque le mot qui se pr�sente comporte 4 zeros on s'aper�oit que le premier zero est trait� normalement puis l'on sort de la boucle. En fait BX comportant le contenu du mot � afficher, ce dernier est shift� � droite 4 fois apr�s chaque digit trait� ( LOOP situ� en CS:5933 ). Donc le premier 0 devient 30 ( ASCII ) puis BX contenant 0000 shift� cel� fait toujours 0000. Et le test OR BX,DX donnant zero on sort par CS:593A. R�sultat pratique l'affichage de 0000 se traduit par un 0 sur l'�cran. Dans le cas ou BX contient 3BC1 ( 4 digits HEXA ) on a beau shifter 4 fois � chaque digit le contenu de BX ne sera pas nul avant que l'on ai trait� tous les chiffres. Donc 0000 donne 0 000C donne C 00AD donne AD 032A donne 32A Conclusion. ----------- Il faut donc que dans tous les cas la boucle se fasse quatre fois sans interruption. Pour cel� il est n�cessaire d'agir sur le test en 1AD7:593A. Yaka ! La premi�re id�e qui vient c'est d'agir sur le contenu d'un des deux registres que l'instruction JNZ teste. Bonne id�e mais quasi ir�alisable car d'une part BX contient le mot de 16 bits � afficher et DX une valeur impossible � modifier sans obtenir un affichage erratique. L'initiali- sation de ce registre doit rester ce qu'elle �tait depuis 1AD7:5901 ( initialisation de DX ). Il faut donc utiliser une variable externe � cette boucle que l'on initialise � quatre puis que l'on d�cr�mente chaque fois qu'un digit est trait�. Plus facile � dire qu'� faire parceque cel� prendra au moins 4 � 5 instructions que je ne vois pas o� loger. La seule fa�on de faire c'est de trouver une zone inexploit�e, y sauter tester la variable en question puis revenir � la boucle de test originelle. Si le programme avait �t� �crit en ASM il aurait �t� difficile de trouver une zone libre, alors que dans le cas pr�sent il suffit de r�cup�rer une des zones inexploit�es par le passage au linker. Il est facile de les trouver puisqu'elles sont en mode texte. Pour ne pas compliquer la programmation il vaut mieux essayer de trouver une de ces zones dans le segment courant. Une de ces zone existe en CS:8E85. La premi�re chose � faire est d'y sauter en y reportant toutes les instructions �cras�es par le saut. Dans le cas pr�sent je d�cide de placer un saut juste avant le test JNZ en 1AD7:5937. Ce saut prend 3 octets, je vais donc �craser les deux instructions OR BX,DX ainsi que le POP BX. Il faudra donc restaurer ces deux instructions avant de revenir au saut JNZ en CS:593A. Voici ci-dessous la boucle modifi�e: CS=1AD7 CS:591E FD STD ��> CS:591F 8AC3 MOV AL,BL � CS:5921 22C5 AND AL,CH � CS:5923 0490 ADD AL,90 � CS:5925 27 DAA � CS:5926 1440 ADC AL,40 � CS:5928 27 DAA � CS:5929 AA STOSB � CS:592A FEC4 INC AH � CS:592C 51 PUSH CX � CS:592D 32ED XOR CH,CH � CS:592F D1EA SHR DX,1 <ͻ ; On SHIFT et ROTATE 4 fois � CS:5931 D1DB RCR BX,1 � ; BX et DX. � CS:5933 E2FA LOOP 592F >ͼ � CS:5935 59 POP CX � CS:5936 E94B35 JMP 8E85 ; Je saute dans la zone texte. ��< CS:593A 75E3 JNZ 591F : . : . CS:.... Suite J'aurais ( r�flexion faite ) pu charger DS par CS mais ne voulant pas rajouter deux instructions suppl�mentaires PUSH DS et son pendant le POP, j'ai forc� la variable � tester dans le segment CS. Voici donc ce qui permet de tourner quatre fois dans la boucle quelque soit le contenu � afficher: CS:8E85 33DB XOR BX,BX ;JE ME SERT DE BX POUR TESTER LE CS:8E87 2E3A1E9D8E CMP BYTE BX,CS:[8E9D] ;CONTENU DE MA VARIABLE [8E9D]. CS:8E8C 7506 JNZ 8E94 ;SI # 0 ON SAUTE A DECREMENTE. CS:8E8E 2EC6069D8E04 MOV BYTE CS:[8E9D],04 ;SI = 0 ON REPLACE 4 DANS [8E9D]. CS:8E94 2EFE0E9D8E DEC BYTE CS:[8E9D] ;ON DECREMENTE LE CONTENU DE 8E9D. CS:8E99 5B POP BX ;JE REMETS LE [BX] ECRASE. CS:8E9A E99DCA JMP 593A ;JE REVIENS A MA BOUCLE D'ORIGINE. CS:8E9D 04 ; CECI EST MA VARIABLE SITUEE DANS LE SEGMENT CODE. Bien s�r en se cassant la t�te je suis persuad� que d'autres solutions existent, peut-�tre plus simples. FREDDY |